package com.hyt.it.ogt.ykcj.service.grade;

import cn.hutool.core.util.IdcardUtil;
import com.hyt.it.ogt.ykcj.common.constant.Constants;
import com.hyt.it.ogt.ykcj.common.constant.ImportFieldConstants;
import com.hyt.it.ogt.ykcj.common.core.domain.entity.SysUser;
import com.hyt.it.ogt.ykcj.common.enums.BizErrorCode;
import com.hyt.it.ogt.ykcj.common.exception.CustomException;
import com.hyt.it.ogt.ykcj.common.utils.CardUtil;
import com.hyt.it.ogt.ykcj.common.utils.DateUtils;
import com.hyt.it.ogt.ykcj.common.utils.IdUtils;
import com.hyt.it.ogt.ykcj.common.utils.StringUtils;
import com.hyt.it.ogt.ykcj.domain.grade.ExamineeGradeInfo;
import com.hyt.it.ogt.ykcj.domain.grade.ExamineeSubjectGradeInfo;
import com.hyt.it.ogt.ykcj.domain.vo.ImportGradeInfoVo;
import org.apache.commons.compress.utils.Lists;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 导入抽象类
 * @Author chentb
 * @Date 2021/6/22 13:52
 */

public abstract class ImportAbstractService {
    private  final Logger logger = LoggerFactory.getLogger(this.getClass());
    // 科目名称前缀
    public static final String PREFIX_KM="KM_";

    // 科目通过年份和状态前缀
    public static final String PREFIX_PASS="PASS";

    // 科目通过年份
    public static final String PASS_YEAR="PASSYEAR";

    // 科目通过状态
    public static final String PASS_FLAG="PASS_";

    // 科目通过状态
    public static final int MAX_IMPORT=200;

    /**
     * 批量插入的条数
     */
    public static final int BATCH_COUNT = 3000;

    @Resource(name = "threadPoolTaskExecutor")
    protected  ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private IExamineeGradeInfoService gradeInfoService;

    public abstract List<? extends Object> importFile(MultipartFile file,String examId,String examNameConfigId,String examYear);


    /**
     * 设置考生成绩信息
     * @param gradeInfoVo
     * @param subjectCodeList
     * @param fieldName
     * @param rowValues
     * @param sysUser
     * @throws Exception
     */
    public static void setGradeInfo(ImportGradeInfoVo gradeInfoVo, List<String> subjectCodeList, String[] fieldName, Object[] rowValues, SysUser sysUser, String examId, Map<String,
            ExamineeGradeInfo> gradeInfoMap, Map<String,String> subjectNameMap, String examYear,int rowIndex) throws Exception{
        // 获取每一个字段对应的值
        // 每条数据对应的考试成绩信息
        ExamineeGradeInfo examineeGradeInfo = gradeInfoVo.getExamineeGradeInfo();
        Date curDate = DateUtils.getNowDate();
        examineeGradeInfo.setGradeId(IdUtils.generateId());
        examineeGradeInfo.setExamId(examId);
        examineeGradeInfo.setCreateTime(curDate);
        examineeGradeInfo.setCreateBy(sysUser.getUserName());
        examineeGradeInfo.setUpdateTime(curDate);
        examineeGradeInfo.setUpdateBy(sysUser.getUserName());
        examineeGradeInfo.setArchivedStatus(Constants.ARCHIVED_STATUS_N);
        String[] subject = null;
        boolean KMXHisEmpty = false; //考试序号是否为空

        List<String> fieldNames = new ArrayList<>(5);
        fieldNames.add("JBDM");
        fieldNames.add("KMXH");
        fieldNames.add("ZYDM");
        fieldNames.add("PASSYEAR");
        fieldNames.add("DQ");

        for (int i=0;i<fieldName.length;i++){
            if (i < 12) {
                if ((null == rowValues[i] || StringUtils.isEmpty(rowValues[i].toString()))
                        && !fieldNames.contains(fieldName[i])) {
                    gradeInfoVo.setErrorMsg("第" + rowIndex + "行字段【" + fieldName[i] + "】对应的值为空,请检查！");
                    return;
                }
            }
            // 先通过考生成绩列字段匹配，匹配不到使用科目成绩列字段匹配
            if ("KMXH".equals(fieldName[i])){
                // 该考生没有考试科目成绩
                if (null == rowValues[i] || StringUtils.isEmpty(rowValues[i].toString())){
                    KMXHisEmpty = true;
                    continue;
                }
                subject = rowValues[i].toString().trim().split(",");
                //判断科目是否属于当前科目
                for(String temp : subject)
                {
                    if (!subjectCodeList.contains(temp))
                    {
                        gradeInfoVo.setErrorMsg("第"+rowIndex+"行字段【KMXH】对应的值不属于当前考试,请检查！");
                        return;
                    }
                }
                // 初始化科目成绩列表和基础数据
                initSubjectInfoList(gradeInfoVo,subject,curDate,sysUser,subjectNameMap);
            }
            String importGradeField = ImportFieldConstants.getGradeFieldMap().get(fieldName[i]);
            if (null != importGradeField){
                Field classField = ExamineeGradeInfo.class.getDeclaredField(importGradeField);
                classField.setAccessible(true);
                setFieldValue(examineeGradeInfo,rowValues[i],classField);
            }else{
                // 没有科目序号不解析科目成绩信息
                if (KMXHisEmpty){
                    continue;
                }
                // 学生学科成绩Map
                // 设置科目成绩
                for (int j=1;j<=subjectCodeList.size();j++){
                    // 获取科目
                    String code = subjectCodeList.get(j-1).substring(2);
                    // 匹配到科目序号的数据才做处理
                    if (fieldName[i].endsWith(code) && null != gradeInfoVo.getSubjectInfoMap().get(code)){
                        String mapKey = null;
                        // 科目通过年份(格式:pass1)
                        if (fieldName[i].lastIndexOf(Constants.splitStr) == -1){
                            mapKey = PREFIX_PASS;
                        }else {
                            //  设置科目相关的信息
                            String arr[] = fieldName[i].split(Constants.splitStr);
                            if (arr.length > 1){
                                mapKey = arr[0]+Constants.splitStr;
                            }
                        }
                        if (null == mapKey){
                            gradeInfoVo.setErrorMsg("字段【"+fieldName[i]+"】不存在,请联系管理员");
                            break;
                        }
                        importGradeField = ImportFieldConstants.getSubjectGradeFieldMap().get(mapKey);
                        if (null !=importGradeField && null != gradeInfoVo.getSubjectInfoMap().get(code)){
                            if (!fieldName[i].startsWith(PREFIX_KM)){
                                Field classField = ExamineeSubjectGradeInfo.class.getDeclaredField(importGradeField);
                                classField.setAccessible(true);
                                setFieldValue(gradeInfoVo.getSubjectInfoMap().get(code),rowValues[i],classField);
                                // 为科目列时如果根据科目名称获取值不为空，则设置科目id
//                                String subjectName = rowValues[i].toString().trim();
//                                if (null != subjectNameMap.get(subjectName)){
//                                    gradeInfoVo.getSubjectInfoMap().get(code).setSubjectId(subjectNameMap.get(subjectName));
//                                }else {
//                                    gradeInfoVo.setErrorMsg("科目【"+rowValues[i]+"】不存在,请联系管理员配置");
//                                    break;
//                                }
//                                break;
//                            }else {
//                                Field classField = ExamineeSubjectGradeInfo.class.getDeclaredField(importGradeField);
//                                classField.setAccessible(true);
//                                setFieldValue(gradeInfoVo.getSubjectInfoMap().get(code),rowValues[i],classField);
                            }
                            break;
                        }
                    }
                }
            }
        }
        //examineeGradeInfo.setExamineeAge(30);
        // 设置考试对应的年纪 todo 暂时屏蔽用于测试
        if (!IdcardUtil.isValidCard(examineeGradeInfo.getExamineeIdCode())){
            gradeInfoVo.setErrorMsg("证件号码格式错误");
            return;
        }
        examineeGradeInfo.setExamineeAge(CardUtil.getExamAge(CardUtil.getCurAge(examineeGradeInfo.getExamineeIdCode()),examYear));

        if (!KMXHisEmpty) {
            for (Map.Entry<String, ExamineeSubjectGradeInfo> entry : gradeInfoVo.getSubjectInfoMap().entrySet()) {
                ExamineeSubjectGradeInfo subjectGradeInfo = entry.getValue();
                subjectGradeInfo.setExamId(examId);
                gradeInfoVo.getSubjectGradeInfoList().add(entry.getValue());
            }
        }else {
            gradeInfoVo.getExamineeGradeInfo().setPassYear(null);
            gradeInfoVo.getExamineeGradeInfo().setPassFlag("2");
        }

        // 如果已存在考生成绩信息，则更新
        if (null != gradeInfoMap.get(examineeGradeInfo.getExamineeIdCode())){
            examineeGradeInfo.setGradeId(gradeInfoMap.get(examineeGradeInfo.getExamineeIdCode()).getGradeId());
            for (ExamineeSubjectGradeInfo subjectGradeInfo:gradeInfoVo.getSubjectGradeInfoList()) {
                subjectGradeInfo.setGradeId(gradeInfoMap.get(examineeGradeInfo.getExamineeIdCode()).getGradeId());
            }
        }
    }

    /**
     * 字段设置名称
     * @param obj 设置的对象
     * @param val 设置的字段值
     * @param classField 设置的字段
     * @throws Exception
     */
    public static void setFieldValue(Object obj, Object val, Field classField) throws Exception{
        if (val == null)
            return;
        if (classField.getType() == Integer.class){
            String value = val.toString().trim();
            String arr[] = value.split("\\.");
            classField.set(obj,Integer.parseInt(arr[0]));
        }else if(classField.getType() == String.class){
            String value = val.toString().trim();
            String arr[] = value.split("\\.");
            if (arr.length > 1){
                classField.set(obj,arr[0]);
            }else {
                classField.set(obj,value);
            }
        }else if(classField.getType() == Long.class){
            classField.set(obj,Long.parseLong(val.toString()));
        }else if(classField.getType() == BigDecimal.class){
            System.out.println(classField.getName());
            classField.set(obj,new BigDecimal(val.toString()));
        }

    }

    /**
     * 初始化科目成绩列表
     * @param subjects
     * @return
     */
    public static void initSubjectInfoList(ImportGradeInfoVo gradeInfoVo,String[] subjects,Date curDate,SysUser sysUser,Map<String,String> subjectCodeMap){
        Map<String,ExamineeSubjectGradeInfo> subjectInfoMap  = new HashMap<>();
        for (int i=0;i<subjects.length;i++){
            ExamineeSubjectGradeInfo examineeSubjectGradeInfo = new ExamineeSubjectGradeInfo();
            examineeSubjectGradeInfo.setGradeId(gradeInfoVo.getExamineeGradeInfo().getGradeId());
            examineeSubjectGradeInfo.setSubjectGradeId(IdUtils.generateId());
            examineeSubjectGradeInfo.setArchivedStatus(Constants.ARCHIVED_STATUS_N);
            examineeSubjectGradeInfo.setCreateTime(curDate);
            examineeSubjectGradeInfo.setCreateBy(sysUser.getUserName());
            examineeSubjectGradeInfo.setUpdateTime(curDate);
            examineeSubjectGradeInfo.setUpdateBy(sysUser.getUserName());
            //gradeInfoVo.getSubjectGradeInfoList().add(examineeSubjectGradeInfo);
            if (null != subjectCodeMap.get(subjects[i])){
                examineeSubjectGradeInfo.setSubjectId(subjectCodeMap.get(subjects[i]));
            }
            subjectInfoMap.put(subjects[i].substring(2),examineeSubjectGradeInfo);
        }
        gradeInfoVo.setSubjectInfoMap(subjectInfoMap);
    }

    /**
     * 异步批量插入成绩
     * @param list
     */
    public void batchInsertGrade(List<ImportGradeInfoVo> list) {
        int count = list.size() % BATCH_COUNT == 0 ? list.size() / BATCH_COUNT : list.size() / BATCH_COUNT + 1;
        // 循环创建线程
        CountDownLatch countDownLatch = new CountDownLatch(count);
        List<FutureTask<Boolean>> resultList = Lists.newArrayList();
        for (int i = 0; i < count; i++) {
            List<ImportGradeInfoVo> gradeInfoVos = new ArrayList<>(BATCH_COUNT);
            if (i + 1 == count) {
                gradeInfoVos.addAll(list.subList(i * BATCH_COUNT, list.size()));
            } else {
                gradeInfoVos.addAll(list.subList(i * BATCH_COUNT, (i + 1) * BATCH_COUNT));
            }
            FutureTask<Boolean> future =  new FutureTask<>(new ImportGradeTask(gradeInfoVos,countDownLatch));
            threadPoolTaskExecutor.submit(future);
            //将任务执行结果存储到List中
            resultList.add(future);

        }
        try {
            countDownLatch.await();
            for (FutureTask<Boolean> futureTask : resultList) {
                if(!futureTask.get()){
                    throw new CustomException(BizErrorCode.GRADE_E0006);
                }
            }
            logger.info("异步批量插入成绩任务完成" + DateUtils.localDateMillisToString(LocalDateTime.now()));
        } catch (InterruptedException e) {
            logger.error(e.getCause().getMessage());
            Thread.currentThread().interrupt();
            throw new CustomException(BizErrorCode.GRADE_E0006);
        } catch (ExecutionException e) {
            //看看失败原因
            logger.error(e.getCause().getMessage());
            throw new CustomException(BizErrorCode.GRADE_E0006);
        }
    }

    /**
     * 异步插入成绩处理
     */
    public class ImportGradeTask  implements Callable<Boolean> {

        private List<ImportGradeInfoVo> gradeInfoVos;
        private CountDownLatch countDownLatch;
        public ImportGradeTask(List<ImportGradeInfoVo> gradeInfoVos,CountDownLatch countDownLatch) {
            this.gradeInfoVos = gradeInfoVos;
            this.countDownLatch = countDownLatch ;
        }

        @Override
        public Boolean call(){
            try {
                logger.info("SaveData size: {}", gradeInfoVos.size());
                gradeInfoService.insertGradeInfo(gradeInfoVos);
                return true;
            } finally {
                countDownLatch.countDown();
            }
        }
    }
}
